import path from 'node:path'
import { ParamsType, buildMethodKey } from "../common"
import { BeanFactory, ReflectUtil } from '../di'
import { HttpApplication } from './HttpApplication'
import { Router } from './middleware'
import { IHttpApplication, IHttpContext, IMiddleware, TNext } from "../interface"

export abstract class HttpBootstrap {
  app: IHttpApplication
  config: any
  scanDirs: string[]
  router = new Router()
  container = BeanFactory.getInstance()

  async start() {
    try {
      // 初始化(DB)
      await this.onInit()
      // 读取配置文件
      // const data = fs.readFileSync(configFile).toString()
      // this.config = JSON.parse(data)
      this.config = await this.getConfig() || {}
      this.scanDirs = await this.getScanDirs() || []
      // 加载 bean
      await this.beforeScanDirs()
      await BeanFactory.getInstance().scanDirs(this)
      await this.afterScanDirs()

      this.app = new HttpApplication()
      // 注册中间件
      await this.beforeRegisterMiddlewares()
      await this.registerMiddlewares()
      await this.afterRegisterMiddlewares()
      // 构建 routers
      await this.beforeRegisterRoutes()
      await this.buildRouter()
      await this.afterRegisterRoutes()

      this.app.on("error", this.onError)
      const port = this.config.http?.port || 3000
      this.app.listen(port, () => {
        console.log(`Summer Server is running on http://127.0.0.1:${port}/`)
      })
      await this.onReady()
    } catch (error) {
      await this.onInitError(error)
      await Promise.reject(error)
      throw error
    }
  }

  /**
   * 注册中间件
   */
  private async registerMiddlewares() {
    const middlewares = await this.getMiddlewares()
    if (middlewares == null || middlewares.length === 0) return
    middlewares.forEach(middleware => {
      this.app.use(async (ctx: IHttpContext, next: TNext) => {
        return middleware.use.call(middleware, ctx, next)
      })
    })
  }

  private async buildRouter() {
    const controllers = this.container.getControllers()
    for (const controller of controllers) {
      const { clazz, args: { path: controllerPath }, inst } = controller
      const routers = ReflectUtil.getRouters((clazz as any).prototype)
      for (const routerKey in routers) {
        const { method, path: methodPath, methodName, handle } = routers[routerKey]
        const methodKey = buildMethodKey(clazz, methodName)
        const middleware = async (ctx: IHttpContext, next: TNext) => {
          return handle.call(
            inst,
            ...this.paramsToList(methodKey, ctx, next),
            ctx, next)
        }

        let routerPath: string = path
          .join(controllerPath, methodPath)
          .split(path.sep)
          .join("/")

        if (routerPath[0] !== '/') {
          routerPath = '/' + routerPath
        }
        if (routerPath.length > 1 && routerPath[routerPath.length - 1] === '/') {
          routerPath = routerPath.substring(0, routerPath.length - 1)
        }

        // // 解析url
        // let matcher // 匹配URL函数
        // const subPaths = [] // 存储各集路由
        // const subParams = [] // params参数
        // for (let sub of routerPath.split('/')) {
        //   if (sub[0] === ':') {
        //     subParams.push(sub.slice(1))
        //     sub = '([^/]*)'
        //   }
        //   subPaths.push(sub)
        // }
        //
        // // 解析params
        // if (subParams.length) {
        //   // 创建含params的正则规则
        //   const re = new RegExp(`^${subPaths.join('/')}$`)
        //   matcher = (url) => {
        //     const matches = re.exec(url)
        //     if (matches) {
        //       const params = {}
        //       for (let i = 0; i < subParams.length; ++i) {
        //         params[subParams[i]] = matches[i + 1]
        //       }
        //       return params
        //     }
        //     return null
        //   }
        // } else {
        //   matcher = (url) => (url === path ? {} : null)
        // }

        // const params = matcher(pathname)

        this.router.register(routerPath, method, middleware)
        console.log(`Router register ${method} -- ${routerPath} => ${buildMethodKey(clazz, methodName)}`)
      }
    }

    this.app.use(this.router.routes())
    this.app.use(this.router.allowedMethods())
  }

  // @Param
  private paramsToList(methodKey: string, ctx: IHttpContext, next: TNext) {
    // 不存在key值时直接返回一个空数组
    const params = ReflectUtil.getParams(methodKey)
    if (params == null || params.length === 0) return []
    return params.map(item => {
      switch (item.paramsType) {
        case ParamsType.path:
          return ctx.params[item.paramId]
        case ParamsType.query:
          return ctx.query[item.paramId]
        case ParamsType.header:
          return ctx.headers[item.paramId]
        case ParamsType.headers:
          return ctx.headers
        case ParamsType.request:
          return ctx.request
        case ParamsType.response:
          return ctx.response
        case ParamsType.next:
          return next
        default:
          break
      }
    })
  }

  abstract getConfig(): Promise<any>

  abstract getScanDirs(): Promise<string[]>

  async getMiddlewares(): Promise<Array<IMiddleware>> {
    return []
  }

  async onError(err: Error, ctx: IHttpContext) {
    console.log(err.message)
  }

  async onInit() {
  }

  async beforeScanDirs() {
  }

  async afterScanDirs() {
  }

  async beforeRegisterMiddlewares() {
  }

  async afterRegisterMiddlewares() {
  }

  async beforeRegisterRoutes() {
  }

  async afterRegisterRoutes() {
  }

  async onReady() {
  }

  async onInitError(error: any) {
  }
}
